package com.vcc.web.controller;


import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.stp.StpUtil;
import com.mybatisflex.core.paginate.Page;
import com.vcc.channel.base.model.request.CardCancelDto;
import com.vcc.channel.base.model.request.CardRechargeDto;
import com.vcc.channel.base.model.request.CardWithdrawDto;
import com.vcc.channel.base.model.request.SetTransactionLimitRequest;
import com.vcc.channel.base.model.response.CardInfoVo;
import com.vcc.channel.base.service.ChannelBaseService;
import com.vcc.core.annotations.*;
import com.vcc.core.constant.CardStatusEnum;
import com.vcc.core.constant.CardTypeEnum;
import com.vcc.core.exception.NotFoundException;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.model.StatusEnum;
import com.vcc.core.model.file.userCard.UserCardFilePojo;
import com.vcc.core.util.DateUtil;
import com.vcc.core.util.FileUtil;
import com.vcc.core.util.IdWorker;
import com.vcc.core.util.RegexUtil;
import com.vcc.data.entity.*;
import com.vcc.service.boss.request.userCard.UserCardRechargeDto;
import com.vcc.service.boss.request.userCard.UserCardWalletDto;
import com.vcc.service.boss.response.userCard.UserCardStatisticsVo;
import com.vcc.service.client.dto.userCard.*;
import com.vcc.service.client.userCard.request.ClientUserCardSetLimitRequest;
import com.vcc.service.client.vo.cardholder.CardholderVo;
import com.vcc.service.client.vo.userCard.UpdateUserCardWalletVo;
import com.vcc.service.client.vo.userCard.UserCardInfoVo;
import com.vcc.service.client.vo.userCard.UserCardVo;
import com.vcc.service.service.*;
import com.vcc.web.core.component.RedisComponent;
import com.vcc.web.core.util.UserUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Log4j2
@RestController
@RequestMapping("userCard")
public class UserCardController {

    @Autowired
    private UserCardService userCardService;
    @Autowired
    private UserCardWalletService userCardWalletService;
    @Autowired
    private WalletDetailService walletDetailService;
    @Autowired
    private UserService userService;
    @Autowired
    private CardTypeService cardService;
    @Autowired
    private WalletService walletService;
    @Autowired
    private CardholderService cardholderService;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private CardOrderService cardOrderService;
    @Autowired
    private RedisComponent redisComponent;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ClientParamService clientParamService;
    @Autowired
    private CardGroupService cardGroupService;
    @Autowired
    private ShareAccountConfigService shareAccountConfigService;

    @RestPack
    @GetMapping("get")
    public UserCardVo get(Long id) {
        User user = UserUtil.get();
        UserCard userCard = userCardService.getById(id);
        UserUtil.baseVerifyUser(
                UserUtil.getRole(),
                user.getId(),
                userCard.getUserId(),
                user.getParentId(),
                userService.getParentIdById(userCard.getUserId())
        );
        UserCardWallet userCardWallet = userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId());
        String account = userService.getAccountById(userCard.getUserId());
        Cardholder cardholder = RegexUtil.checkObjectIsNull(userCard.getCardholderId()) ? null : cardholderService.getById(userCard.getCardholderId());
        return new UserCardVo(userCard, userCardWallet, account, cardholder);
    }

    @RestPack
    @GetMapping("paging")
    @SaCheckPermission("card:list:view")
    public Page<UserCardVo> paging(
            @RequestParam(value = "pageNumber", defaultValue = "1") Integer pageNumber,
            @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize,
            Long targetUserId,
            String cardNumberLastFour,
            String startDate,
            String endDate,
            String cardStatus,
            String remark,
            Long groupId,
            Long id,
            String sort
    ) {
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        if (targetUserId != null && targetUserId != 0L) {
            UserUtil.baseVerifyUser(
                    role,
                    user.getId(),
                    targetUserId,
                    user.getParentId(),
                    userService.getParentIdById(targetUserId)
            );
        }
        if (groupId != null && groupId != 0L) {
            CardGroup cardGroup = cardGroupService.getByIdOpt(groupId).orElseThrow(() -> new ProgramException(("没有当前卡组信息")));
            UserUtil.baseVerifyUser(
                    role,
                    user.getId(),
                    cardGroup.getUserId(),
                    user.getParentId(),
                    userService.getParentIdById(cardGroup.getUserId())
            );
        }
        Page<UserCardVo> page = null;
        page = userCardService.paging(
                Page.of(pageNumber, pageSize),
                role.getHasAllData() && parentId != null && parentId != 0 ? parentId : user.getId(),
                role,
                targetUserId,
                cardNumberLastFour,
                remark,
                startDate,
                endDate,
                cardStatus,
                id,
                groupId,
                sort);
        if (page.getRecords().isEmpty()) {
            return page.map(it -> new UserCardVo());
        }
        List<Long> userIds = page.getRecords().stream()
                .map(UserCardVo::getUserId)
                .distinct()
                .toList();
        List<UserCardWallet> userCardWallets = userCardWalletService.listByUserIds(userIds);
        List<ShareAccountConfig> shareAccountConfigs = shareAccountConfigService.listByUserIds(userIds);
        List<Long> cardTypeIds = page.getRecords().stream()
                .map(UserCardVo::getCardTypeId)
                .distinct()
                .toList();
        List<CardType> cardTypes = !cardTypeIds.isEmpty() ? cardService.listByIds(cardTypeIds) : Collections.emptyList();
        List<Long> cardholderIds = page.getRecords().stream()
                .filter(it -> StringUtils.isNotBlank(it.getCardholderId()))
                .map(it -> Long.valueOf(it.getCardholderId()))
                .distinct()
                .toList();
        List<Cardholder> cardholders = !cardholderIds.isEmpty() ? cardholderService.listByIds(cardholderIds) : Collections.emptyList();
        return page.map(it -> {
            Cardholder cardholder = StringUtils.isBlank(it.getCardholderId()) ? null : cardholders.stream()
                    .filter(holder -> holder.getId().equals(Long.valueOf(it.getCardholderId())))
                    .findFirst()
                    .orElse(null);
            CardType cardType = cardTypes.stream()
                    .filter(card -> card.getId().equals(it.getCardTypeId()))
                    .findFirst()
                    .orElse(null);
            if (it.getCardType().equals("normal")) {
                UserCardWallet userCardWallet = userCardWallets.stream()
                        .filter(wallet -> wallet.getUserId().equals(it.getUserId()) && wallet.getCardId().equals(it.getCardTypeId()))
                        .findFirst()
                        .orElse(null);
                it.setVerifyRechargeCardStatus(setUserCardSwitch(cardType == null ? 1 : cardType.getVerifyRechargeCardStatus(), userCardWallet == null ? 1 : userCardWallet.getVerifyRechargeCardStatus()));
                it.setVerifyOutCardStatus(setUserCardSwitch(cardType == null ? 1 : cardType.getVerifyOutCardStatus(), userCardWallet == null ? 1 : userCardWallet.getVerifyOutCardStatus()));
                it.setVerifyCloseCardStatus(setUserCardSwitch(cardType == null ? 1 : cardType.getVerifyCloseCardStatus(), userCardWallet == null ? 1 : userCardWallet.getVerifyCloseCardStatus()));
                it.setVerifyFreezeUnFreezeCardStatus(setUserCardSwitch(cardType == null ? 1 : cardType.getVerifyFreezeUnFreezeCardStatus(), userCardWallet == null ? 1 : userCardWallet.getVerifyFreezeUnFreezeCardStatus()));
            } else {
                ShareAccountConfig shareAccountConfig = shareAccountConfigs.stream().filter(config -> config.getCardTypeId().equals(it.getCardTypeId()) && config.getUserId().equals(it.getUserId())).findFirst().orElse(null);
                it.setVerifyUpdateLimitStatus(setUserCardSwitch(cardType == null ? 1 : cardType.getVerifyRechargeCardStatus(), shareAccountConfig == null ? 1 : shareAccountConfig.getVerifyUpdateLimitStatus()));
                it.setVerifyCloseCardStatus(setUserCardSwitch(cardType == null ? 1 : cardType.getVerifyCloseCardStatus(), shareAccountConfig == null ? 1 : shareAccountConfig.getVerifyCloseCardStatus()));
                it.setVerifyFreezeUnFreezeCardStatus(setUserCardSwitch(cardType == null ? 1 : cardType.getVerifyFreezeUnFreezeCardStatus(), shareAccountConfig == null ? 1 : shareAccountConfig.getVerifyFreezeUnFreezeCardStatus()));
            }
            //设置基本配置
            it.setOpenCardRechargeMinAmount(cardType == null ? BigDecimal.valueOf(0.01) : cardType.getOpenCardRechargeMinAmount());
            it.setWithdrawMinAmount(cardType == null ? BigDecimal.valueOf(0.01) : cardType.getWithdrawMinAmount());
            it.setRechargeMinAmount(cardType == null ? BigDecimal.valueOf(0.01) : cardType.getRechargeMinAmount());
            it.setCloseCardMinAmount(cardType == null ? BigDecimal.valueOf(0.01) : cardType.getCloseCardMinAmount());
            it.setWithdrawStatus(cardType == null ? CardStatusEnum.ACTIVE.getCode() : cardType.getWithdrawStatus());
            it.setCloseStatus(cardType == null ? CardStatusEnum.ACTIVE.getCode() : cardType.getCloseStatus());
            //设置钱包相关 优先检查cardType对应数据。如果没有则使用userCardWallet对应数据 如果cardType中对应关闭则直接关闭，如果开启则检查userCardWallet的开关

            it.setFirstName(RegexUtil.checkObjectIsNull(cardholder) ? null : cardholder.getFirstName());
            it.setLastName(RegexUtil.checkObjectIsNull(cardholder) ? null : cardholder.getLastName());
            it.setChannelCode(null);
            return it;
        });
    }

    //0 支持 1 2 不支持
    public Integer setUserCardSwitch(Integer cardSwitch, Integer userCardWalletSwitch) {
        if (cardSwitch == 1 || cardSwitch == 2) {
            return 1;
        } else {
            return userCardWalletSwitch;
        }
    }


    @RestPack
    @GetMapping("derive")
    @SaCheckPermission("card:list:derive")
    public void derive(
            @CurrentROStatus Boolean roStatus,
            Long targetUserId,
            String cardNumberLastFour,
            String startDate,
            String endDate,
            String cardStatus,
            String remark,
            Long id,
            String sort,
            Long groupId,
            HttpServletResponse response
    ) {
        if (StringUtils.isBlank(startDate) || StringUtils.isBlank(endDate)) {
            throw new ProgramException("请选择时间区间");
        }
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        boolean hasSecret = StpUtil.hasPermission("card:list:derive:secret");
        if (hasSecret) {
            if (!StpUtil.isSafe("CARD_LIST_DERIVE")) {
                throw new ProgramException(StatusEnum.UN_SAFE, "需要进行二次验证");
            }
        }
        if (targetUserId != null && targetUserId != 0L) {
            UserUtil.baseVerifyUser(
                    role,
                    user.getId(),
                    targetUserId,
                    user.getParentId(),
                    userService.getParentIdById(targetUserId)
            );
        }
        if (groupId != null && groupId != 0L) {
            CardGroup cardGroup = cardGroupService.getByIdOpt(groupId).orElseThrow(() -> new ProgramException(("没有当前卡组信息")));
            UserUtil.baseVerifyUser(
                    role,
                    user.getId(),
                    cardGroup.getUserId(),
                    user.getParentId(),
                    userService.getParentIdById(cardGroup.getUserId())
            );
        }
        SXSSFWorkbook sxssfWorkbook = FileUtil.createUserCardTitle(1000);
        Page<UserCard> paging;
        Integer pages = 1;
        do {
            paging = userCardService.paging(
                    pages,
                    500,
                    role.getHasAllData() && parentId != null && parentId != 0 ? parentId : user.getId(),
                    role,
                    targetUserId,
                    cardNumberLastFour,
                    remark,
                    startDate,
                    endDate,
                    cardStatus,
                    id,
                    groupId,
                    sort);
            List<Long> userIds = paging.getRecords().stream().map(UserCard::getUserId).distinct().toList();
            List<User> users = userService.listByIds(userIds);
            List<UserCardFilePojo> map = paging.getRecords().stream().map(it -> {
                Cardholder cardholder = RegexUtil.checkObjectIsNull(it.getCardholderId()) ? null : cardholderService.getById(it.getCardholderId());
                User userCardUser = users.stream()
                        .filter(temp -> temp.getId().equals(it.getUserId()))
                        .findFirst()
                        .orElse(null);
                return new UserCardFilePojo(
                        it.getId(),
                        userCardUser == null ? "" : userCardUser.getAccount(),
                        it.getCardNumber(),
                        hasSecret ? it.getVcc() : "***",
                        hasSecret ? it.getExpiry() : "****-**",
                        it.getUsableQuota(),
                        it.getRemark(),
                        DateUtil.format(it.getRequestDate()),
                        it.getCardStatus(),
                        cardholder == null ? "" : cardholder.getFirstName(),
                        cardholder == null ? "" : cardholder.getLastName());
            }).toList();
            FileUtil.deriveCardData(sxssfWorkbook, map, paging.getPageNumber(), paging.getPageSize(), false, roStatus);
        } while (FileUtil.hasNextPage(pages.longValue(), 500L, paging.getTotalPage()));
        String fileName = System.currentTimeMillis() + "卡详情.xlsx";
        FileUtil.downXlsxFile(response, sxssfWorkbook, fileName);
    }

    @RestPack
    @GetMapping("listUserCardWallet")
    @SaCheckPermission("card:config:view")
    public List<UpdateUserCardWalletVo> listUserCardWallet(
            Long targetUserId
    ) {
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        User targetUser = userService.getById(targetUserId);
        if (!role.getHasAllData()) {
            if (targetUserId != null && targetUserId != 0) {
                throw new ProgramException("您没有查看其他用户的权限");
            }
        } else {
            //parentId != 0 则是子账户
            if (parentId != 0) {
                if (targetUser.getParentId() != 0L && !targetUser.getParentId().equals(parentId)) {
                    throw new ProgramException("当前用户不归属与您无法查询");
                }
            } else {
                if (targetUser.getParentId() != 0L && !targetUser.getParentId().equals(user.getId())) {
                    throw new ProgramException("当前用户不归属与您无法查询");
                }
            }
        }
        List<CardType> list = cardService.list();
        List<UserCardWallet> userCardWallets = userCardWalletService.listByUserId(targetUserId);
        return userCardWallets
                .stream()
                .map(it ->
                        new UpdateUserCardWalletVo(
                                list.stream()
                                        .filter(card -> card.getId().equals(it.getCardId()))
                                        .findFirst()
                                        .orElseThrow(() -> new NotFoundException("没有当前卡信息")),
                                it)
                ).collect(Collectors.toList());
    }

    @RestPack
    @ROReject
    @KycReject
    @RepeatSubmit
    @SaCheckPermission("card:config:edit")
    @PostMapping("updateUserCardWallet")
    public Boolean updateUserCardWallet(
            @RequestBody UserCardWalletDto userCardWalletDto
    ) {
        if (Objects.equals(userCardWalletDto.getAmount().compareTo(BigDecimal.valueOf(0)), -1)) {
            throw new ProgramException("开卡费调整失败，金额不能小于0");
        }
        if (userCardWalletDto.getOpenCardOnLineCount() < 1) {
            throw new ProgramException("单次开卡上线调整失败，次数不能小于1");
        }
        UserCardWallet userCardWallet = userCardWalletService.getById(userCardWalletDto.getId());
        Long userId = UserUtil.getId();
        User user = userService.getById(userCardWallet.getUserId());
        if (!user.getBeLongToDealer().equals(userId)) {
            throw new ProgramException("当前用户不归属与您无法查询");
        }
        CardType card = cardService.getById(userCardWallet.getCardId());
        if (Objects.equals(userCardWalletDto.getAmount().compareTo(card.getDealerMinAmount()), -1)) {
            throw new ProgramException("开卡费低于经销商最低开卡费");
        }
        return userCardWalletService.updateInfo(userCardWalletDto);
    }

    @RestPack
    @ROReject
    @KycReject
    @RepeatSubmit
    @SaCheckPermission("card:list:remark:edit")
    @PostMapping("updateRemark")
    public Boolean updateRemark(
            @RequestBody UpdateRemarkDto updateRemarkDto
    ) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        UserCard userCard = userCardService.getById(updateRemarkDto.getId());
        UserUtil.baseVerifyUser(
                role,
                user.getId(),
                userCard.getUserId(),
                user.getParentId(),
                userService.getParentIdById(userCard.getUserId())
        );
        return userCardService.updateRemarkById(userCard.getId(), updateRemarkDto.getRemark());
    }

    @RestPack
    @GetMapping("getCardBalance")
    @SaCheckLogin
    @RepeatSubmit
    public BigDecimal getCardBalance(
            Long id) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        UserCard userCard = userCardService.getById(id);
        UserUtil.baseVerifyUser(
                UserUtil.getRole(),
                user.getId(),
                userCard.getUserId(),
                user.getParentId(),
                userService.getParentIdById(userCard.getUserId())
        );
        //TODO  调用API
        ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
        CardInfoVo cardInfoVo = channelBaseService.queryCardInfo(userCard.getChannelCardId());
        userCardService.updateUsableQuotaAndTotalLimitByChannelCardId(
                cardInfoVo.getBalance(),
                cardInfoVo.getTotalLimit(),
                cardInfoVo.getCardStatus(),
                userCard.getChannelCardId()
        );
        return cardInfoVo.getBalance();
    }

    @RestPack
    @ROReject
    @KycReject
    @RepeatSubmit
    @PostMapping("recharge")
    @SaCheckPermission("card:list:recharge")
    public Boolean recharge(
            @RequestBody @Validated UserCardRechargeDto userCardRechargeDto) {
        if (userCardRechargeDto.getAmount().compareTo(BigDecimal.valueOf(0)) <= 0) {
            throw new ProgramException("金额必须大于0");
        }
        RLock lock = redissonClient.getLock("userCard:recharge:" + UserUtil.getId());
        try {
            boolean tryLock = lock.tryLock();
            if (!tryLock) {
                throw new ProgramException("请勿重复提交订单");
            }
            User user = UserUtil.get();
            Role role = UserUtil.getRole();
            UserCard userCard = userCardService.getById(userCardRechargeDto.getId());
            if (userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                throw new ProgramException("共享账号不支持转出");
            }
            UserUtil.baseVerifyUser(
                    UserUtil.getRole(),
                    user.getId(),
                    userCard.getUserId(),
                    user.getParentId(),
                    userService.getParentIdById(userCard.getUserId())
            );
            CardType card = cardService.getById(userCard.getCardTypeId());
            baseVerifyStatus(card.getVerifyRechargeCardStatus(), 0, "当前卡种禁止充值");
            UserCardWallet userCardWallet = userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId());
            baseVerifyStatus(userCardWallet.getVerifyRechargeCardStatus(), 0, "当前卡种禁止充值");
            Wallet wallet = walletService.getByUserIdAndCurrencyCode(userCard.getUserId(), userCard.getCurrencyCode());
            BigDecimal tempSumRechargeAmount = userCardRechargeDto.getAmount();
            //充值手续费
            if (Objects.equals(userCardWallet.getExchangeRate().compareTo(BigDecimal.valueOf(0)), 1)) {
                BigDecimal tempExchangeRateAmount = userCardWallet.getExchangeRate()
                        .divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP)
                        .multiply(userCardRechargeDto.getAmount())
                        .setScale(2, RoundingMode.HALF_UP);
                tempSumRechargeAmount = tempSumRechargeAmount.add(tempExchangeRateAmount);
            }
            if (tempSumRechargeAmount.compareTo(wallet.getUsableQuota()) > 0) {
                throw new ProgramException("您" + userCard.getCurrencyName() + "钱包余额不足");
            }
            //TODO  调用API
            String orderId = IdWorker.getStringOrderId();
            walletService.cardRecharge(
                    userCard.getUserId(),
                    user.getId(),
                    card.getCurrencyCode(),
                    orderId,
                    card.getId(),
                    userCard.getCardNumber(),
                    userCard.getId(),
                    card.getChannelName(),
                    userCardRechargeDto.getAmount(),
                    userCardRechargeDto.getRemark(),
                    "CLIENT"
            );
            cardOrderService.rechargeCard(
                    orderId,
                    userCard.getCardTypeId(),
                    userCard.getUserId(),
                    UserUtil.getId(),
                    userCard.getCardNumber(),
                    userCard.getId(),
                    userCard.getChannelCode(),
                    userCardRechargeDto.getAmount(),
                    userCard.getCurrencyCode(),
                    userCardRechargeDto.getRemark(),
                    "CLIENT"
            );
            ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
            channelBaseService.recharge(
                    CardRechargeDto.builder()
                            .channelCardId(userCard.getChannelCardId())
                            .rechargeAmount(userCardRechargeDto.getAmount())
                            .requestId(orderId)
                            .cardNumberId(userCard.getId())
                            .cardNumber(userCard.getCardNumber())
                            .build()
            );
        } catch (Exception e) {
            if (e instanceof ProgramException) {
                throw e;
            }
            throw new ProgramException("充值失败，请稍后再试");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }

    @RestPack
    @ROReject
    @RepeatSubmit
    @KycReject
    @PostMapping("out")
    @SaCheckPermission("card:list:withdraw")
    public Boolean out(
            @RequestBody UserCardOutDto userCardOutDto) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        if (userCardOutDto.getAmount().compareTo(BigDecimal.valueOf(0)) <= 0) {
            throw new ProgramException("金额必须大于0");
        }
        RLock lock = redissonClient.getLock("userCard:out:" + user.getId());
        try {
            boolean tryLock = lock.tryLock();
            if (!tryLock) {
                throw new ProgramException("请勿重复提交订单");
            }
            UserCard userCard = userCardService.getById(userCardOutDto.getId());
            if (userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
                throw new ProgramException("共享账号不支持转出");
            }
            UserUtil.baseVerifyUser(
                    UserUtil.getRole(),
                    user.getId(),
                    userCard.getUserId(),
                    user.getParentId(),
                    userService.getParentIdById(userCard.getUserId())
            );
            CardType card = cardService.getById(userCard.getCardTypeId());
            baseVerifyStatus(card.getVerifyOutCardStatus(), 0, "当前卡种禁止转出");
            UserCardWallet userCardWallet = userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId());
            baseVerifyStatus(userCardWallet.getVerifyOutCardStatus(), 0, "当前卡种禁止转出");
            //TODO 获取实际卡余额
            ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
            CardInfoVo cardInfoVo = channelBaseService.queryCardInfo(userCard.getChannelCardId());
            if (cardInfoVo.getBalance().compareTo(userCardOutDto.getAmount()) < 0) {
                userCardService.updateUsableQuotaById(cardInfoVo.getBalance(), userCard.getId());
                throw new ProgramException("您当前卡余额不足，无法转出");
            }
            if (!cardInfoVo.getCardStatus().equals(card.getWithdrawStatus())) {
                throw new ProgramException("当前卡状态为" + CardStatusEnum.fromCode(cardInfoVo.getCardStatus()).getDesc() + "，无法转出,当前卡段仅支持在" + CardStatusEnum.fromCode(card.getWithdrawStatus()).getDesc() + "状态下转出");
            }
            String orderId = IdWorker.getStringOrderId();
            cardOrderService.withdrawCard(
                    orderId,
                    userCard.getCardTypeId(),
                    userCard.getUserId(),
                    UserUtil.getId(),
                    userCard.getCardNumber(),
                    userCard.getId(),
                    userCard.getChannelCode(),
                    userCardOutDto.getAmount(),
                    userCard.getCurrencyCode(),
                    userCardOutDto.getRemark(),
                    "CLIENT"
            );
            channelBaseService.withdraw(
                    CardWithdrawDto.builder()
                            .channelCardId(userCard.getChannelCardId())
                            .withdrawAmount(userCardOutDto.getAmount())
                            .requestId(orderId)
                            .cardNumberId(userCard.getId())
                            .cardNumber(userCard.getCardNumber())
                            .build()
            );
        } catch (Exception e) {
            if (e instanceof ProgramException) {
                throw e;
            }
            throw new ProgramException("转出失败，请稍后再试");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }

    @RestPack
    @ROReject
    @KycReject
    @RepeatSubmit
    @PostMapping("freezeAndUnFreeze")
    @SaCheckPermission("card:list:freezeOrUnFreeze")
    @SaCheckLogin
    public Boolean freezeAndUnFreeze(
            @RequestBody @Validated UserCardFreezeAndUnFreezeDto userCardFreezeAndUnFreezeDto) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        RLock lock = redissonClient.getLock("userCard:freezeAndUnFreeze:" + user.getId());
        try {
            boolean tryLock = lock.tryLock();
            if (!tryLock) {
                throw new ProgramException("请勿重复提交订单");
            }
            UserCard userCard = userCardService.getById(userCardFreezeAndUnFreezeDto.getId());
            UserUtil.baseVerifyUser(
                    UserUtil.getRole(),
                    user.getId(),
                    userCard.getUserId(),
                    user.getParentId(),
                    userService.getParentIdById(userCard.getUserId())
            );
            CardType card = cardService.getById(userCard.getCardTypeId());
            baseVerifyStatus(card.getVerifyFreezeUnFreezeCardStatus(), 0, "当前卡种禁止冻结与转出");
            if (userCard.getCardType().equals(CardTypeEnum.NORMAL.getCode())) {
                UserCardWallet userCardWallet = userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId());
                baseVerifyStatus(userCardWallet.getVerifyFreezeUnFreezeCardStatus(), 0, "当前卡种禁止冻结与转出");
            } else {
                ShareAccountConfig shareAccountConfig = shareAccountConfigService.getByUserIdAndCardTypeId(userCard.getUserId(), userCard.getCardTypeId());
                baseVerifyStatus(shareAccountConfig.getVerifyFreezeUnFreezeCardStatus(), 0, "当前卡种禁止冻结与转出");
            }
            //TODO 获取实际卡状态
            //true 冻结  false 解冻
            ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
            String cardStatus = channelBaseService.queryCardStatus(userCard.getChannelCardId());
            String requestId = IdWorker.getStringOrderId();
            switch (cardStatus) {
                case "INACTIVE" -> throw new ProgramException("当前卡状态为未激活，无法冻结或解冻");
                case "ACTIVE" -> {
                    if (userCardFreezeAndUnFreezeDto.getStatus()) {
                        //冻结1
                        cardOrderService.blockCard(
                                requestId,
                                userCard.getCardTypeId(),
                                userCard.getUserId(),
                                UserUtil.getId(),
                                userCard.getCardNumber(),
                                userCard.getId(),
                                userCard.getChannelCode(),
                                "冻结卡",
                                "CLIENT"
                        );
                        channelBaseService.blockCard(userCard.getChannelCardId(), requestId);
                        userCardService.updateCardStatusById(CardStatusEnum.BLOCKED.getCode(), userCard.getId());
                    } else {
                        userCardService.updateCardStatusById(cardStatus, userCard.getId());
                    }
                    break;
                }
                case "BLOCKED" -> {
                    if (!userCardFreezeAndUnFreezeDto.getStatus()) {
                        cardOrderService.unblockCard(
                                requestId,
                                userCard.getCardTypeId(),
                                userCard.getUserId(),
                                UserUtil.getId(),
                                userCard.getCardNumber(),
                                userCard.getId(),
                                userCard.getChannelCode(),
                                "解冻卡",
                                "CLIENT"
                        );
                        channelBaseService.unblockCard(userCard.getChannelCardId(), requestId);
                        userCardService.updateCardStatusById(CardStatusEnum.ACTIVE.getCode(), userCard.getId());
                    } else {
                        userCardService.updateCardStatusById(cardStatus, userCard.getId());
                    }
                    break;
                }
                case "CONTROL" -> {
                    userCardService.updateCardStatusById(cardStatus, userCard.getId());
                    throw new ProgramException("当前卡状态为管控，无法冻结或解冻");
                }
                case "CLOSE", "ABNORMAL" -> {
                    userCardService.updateCardStatusById(cardStatus, userCard.getId());
                    throw new ProgramException("当前卡状态为关卡或异常，无法冻结或解冻");
                }
            }
        } catch (Exception e) {
            if (e instanceof ProgramException) {
                throw e;
            }
            throw new ProgramException("冻结或解冻失败，请稍后再试");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        //调用API冻结
        return true;
    }


    @RestPack
    @ROReject
    @KycReject
    @RepeatSubmit
    @PostMapping("close")
    @SaCheckLogin
    @SaCheckPermission("card:list:close")
    public Boolean close(
            @RequestBody @Validated UserCardCloseDto userCardCloseDto) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        RLock lock = redissonClient.getLock("userCard:close:" + user.getId());
        try {
            boolean tryLock = lock.tryLock();
            if (!tryLock) {
                throw new ProgramException("请勿重复提交订单");
            }
            UserCard userCard = userCardService.getById(userCardCloseDto.getId());
            UserUtil.baseVerifyUser(
                    UserUtil.getRole(),
                    user.getId(),
                    userCard.getUserId(),
                    user.getParentId(),
                    userService.getParentIdById(userCard.getUserId())
            );
            CardType card = cardService.getById(userCard.getCardTypeId());
            baseVerifyStatus(card.getVerifyCloseCardStatus(), 0, "当前卡种禁止销卡");
            if (userCard.getCardType().equals(CardTypeEnum.NORMAL.getCode())) {
                UserCardWallet userCardWallet = userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId());
                baseVerifyStatus(userCardWallet.getVerifyCloseCardStatus(), 0, "当前卡种禁止销卡");
            } else {
                ShareAccountConfig shareAccountConfig = shareAccountConfigService.getByUserIdAndCardTypeId(userCard.getUserId(), userCard.getCardTypeId());
                baseVerifyStatus(shareAccountConfig.getVerifyCloseCardStatus(), 0, "当前卡种禁止销卡");
            }
            ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
            CardInfoVo cardInfoVo = channelBaseService.queryCardInfo(userCard.getChannelCardId());
            if (!cardInfoVo.getCardStatus().equals(card.getCloseStatus())) {
                throw new ProgramException("当前卡状态为" + CardStatusEnum.fromCode(cardInfoVo.getCardStatus()).getDesc() + "，无法销卡,当前卡段仅支持在" + CardStatusEnum.fromCode(card.getCloseStatus()).getDesc() + "状态下销卡");
            }
            if (cardInfoVo.getBalance().compareTo(card.getWithdrawMinAmount()) > 0) {
                userCardService.updateUsableQuotaById(cardInfoVo.getBalance(), userCard.getId());
                throw new ProgramException("当前卡余额大于销卡最低金额" + card.getWithdrawMinAmount() + "，无法销卡");
            }
            String orderId = IdWorker.getStringOrderId();
            cardOrderService.closeCard(
                    orderId,
                    userCard.getCardTypeId(),
                    userCard.getUserId(),
                    UserUtil.getId(),
                    userCard.getCardNumber(),
                    userCard.getId(),
                    userCard.getChannelCode(),
                    "销卡",
                    "CLIENT"
            );
            channelBaseService.cancelCard(
                    CardCancelDto.builder()
                            .channelCardId(userCard.getChannelCardId())
                            .requestId(orderId)
                            .build()
            );
        } catch (Exception e) {
            if (e instanceof ProgramException) {
                throw e;
            }
            throw new ProgramException("销卡失败，请稍后再试");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }

    /**
     * @param status        原始值
     * @param adjustedValue 校验值
     * @param message       提示
     */
    private void baseVerifyStatus(Integer status, Integer adjustedValue, String message) {
        if (!status.equals(adjustedValue)) {
            throw new ProgramException(message);
        }
    }

    @RestPack
    @ROReject
    @KycReject
    @SaCheckLogin
    @RepeatSubmit
    @GetMapping("showUserCardInfo")
    @SaCheckSafe("SHOW_CARD_INFO")
    @SaCheckPermission("card:list:info:view")
    public UserCardInfoVo showUserCardInfo(
            Long id
    ) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        UserCard userCard = userCardService.getById(id);
        UserUtil.baseVerifyUser(
                UserUtil.getRole(),
                user.getId(),
                userCard.getUserId(),
                user.getParentId(),
                userService.getParentIdById(userCard.getUserId())
        );
        CardholderVo cardholderVo = RegexUtil.checkObjectIsNotNull(userCard.getCardholderId()) ?
                new CardholderVo(cardholderService.getById(userCard.getCardholderId())) :
                null;
        return new UserCardInfoVo(userCard, cardholderVo);
    }


    @RestPack
    @SaCheckLogin
    @RepeatSubmit
    @GetMapping("statistics")
    public UserCardStatisticsVo statistics() {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        String openCardLimit = clientParamService.getValueByCodeAndUserIdOrDefaultValue("OPEN_CARD_LIMIT", user.getId(), "0");
        UserCardStatisticsVo userCardStatisticsVo = userCardService.countByUserId(user.getId(), role);
        userCardStatisticsVo.setOpenCardLimit(Integer.parseInt(openCardLimit));
        return userCardStatisticsVo;
    }


    @RestPack
    @SaCheckLogin
    @RepeatSubmit
    @GetMapping("getMaxOpenCardCount")
    public UserCardStatisticsVo getMaxOpenCardCount() {
        User user = UserUtil.get();
        String openCardLimit = clientParamService.getValueByCodeAndUserIdOrDefaultValue("OPEN_CARD_LIMIT", user.getId(), "0");
        Long count = userCardService.countByUserId(user.getId());
        return UserCardStatisticsVo.builder()
                .openCardLimit(Integer.parseInt(openCardLimit))
                .useCardCount(count)
                .build();
    }


    @RestPack
    @SaCheckLogin
    @RepeatSubmit
    @PostMapping("setUserCardGroup")
    @SaCheckPermission("card:list:group:edit")
    @SaCheckSafe("SET_USER_CARD_GROUP")
    public Boolean setUserCardGroup(@RequestBody @Validated SetUserCardGroupDto setUserCardGroupDto) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        List<UserCard> userCards = userCardService.listByIds(setUserCardGroupDto.getIds());
        if (userCards.isEmpty()) {
            throw new ProgramException("请选择需要设置的卡片");
        }
        CardGroup cardGroup = cardGroupService.getByIdOpt(setUserCardGroupDto.getGroupId()).orElseThrow(() -> new NotFoundException("卡组不存在"));
        List<Long> list = userCards.stream().map(UserCard::getUserId).distinct().toList();
        if (list.size() != 1) {
            throw new ProgramException("只能设置同一用户的卡片");
        }
        Long targetUserId = list.get(0);
        //校验分组和卡片是否同一用户
        if (!Objects.equals(targetUserId, cardGroup.getUserId())) {
            throw new ProgramException("卡片与卡组不属于同一用户，无法设置");
        }
        UserUtil.baseVerifyUser(
                role,
                user.getId(),
                targetUserId,
                user.getParentId(),
                userService.getParentIdById(targetUserId)
        );
        for (UserCard userCard : userCards) {
            if (Objects.equals(userCard.getCardGroupId(), cardGroup.getId())) {
                log.info("卡片{}已经在当前卡组内，无需重复设置", userCard.getCardNumber());
                continue;
            }
            userCardService.updateGroupIdIdById(userCard.getId(), userCard.getCardGroupId(), cardGroup.getId());
        }
        return true;
    }


    @RestPack
    @SaCheckLogin
    @RepeatSubmit
    @PostMapping("setUserCardLimit")
    @SaCheckPermission("card:list:limit")
    public boolean setUserCardLimit(@RequestBody @Validated ClientUserCardSetLimitRequest clientUserCardSetLimitRequest) {
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        UserCard userCard = userCardService.getByIdOpt(clientUserCardSetLimitRequest.getId()).orElseThrow(() -> new NotFoundException("卡片不存在"));
        UserUtil.baseVerifyUser(
                role,
                user.getId(),
                userCard.getUserId(),
                user.getParentId(),
                userService.getParentIdById(userCard.getUserId())
        );
        if (!userCard.getCardType().equals(CardTypeEnum.SHARE.getCode())) {
            throw new ProgramException("当前卡片不是共享账号，无法设置额度");
        }
        ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
        CardInfoVo cardInfoVo = channelBaseService.queryCardInfo(userCard.getChannelCardId());
        if (!cardInfoVo.getCardStatus().equals(CardStatusEnum.ACTIVE.getCode())) {
            userCardService.updateCardStatusById(cardInfoVo.getCardStatus(), userCard.getId());
            throw new ProgramException("当前卡状态为" + Objects.requireNonNull(CardStatusEnum.fromCode(cardInfoVo.getCardStatus())).getDesc() + "，无法设置额度");
        }
        if (!clientUserCardSetLimitRequest.getStatus()) {
            //计算可调低的最大额度是多少
            if (clientUserCardSetLimitRequest.getLimit().compareTo(cardInfoVo.getBalance()) > 0) {
                throw new ProgramException("当前卡余额为" + cardInfoVo.getBalance() + "，无法调低至" + clientUserCardSetLimitRequest.getLimit() + "，请重新输入" + cardInfoVo.getBalance() + "及以下的额度");
            }
        }
        String orderId = IdWorker.getStringOrderId();
        //设置额度
        cardOrderService.setLimit(
                userCard.getShareAccountId(),
                orderId,
                userCard.getCardTypeId(),
                userCard.getUserId(),
                UserUtil.getId(),
                userCard.getCardNumber(),
                userCard.getId(),
                userCard.getChannelCode(),
                clientUserCardSetLimitRequest.getLimit(),
                userCard.getCurrencyCode(),
                clientUserCardSetLimitRequest.getStatus() ? "调高额度" : "调低额度",
                "CLIENT"
        );
        channelBaseService.setTransactionLimit(
                SetTransactionLimitRequest.builder()
                        .channelCardId(userCard.getChannelCardId())
                        .requestId(orderId)
                        .status(clientUserCardSetLimitRequest.getStatus())
                        .limit(clientUserCardSetLimitRequest.getLimit())
                        .build()
        );
        userCardService.updateUsableQuotaAndTotalLimitByChannelCardId(
                clientUserCardSetLimitRequest.getStatus() ? cardInfoVo.getBalance().add(clientUserCardSetLimitRequest.getLimit()) : cardInfoVo.getBalance().subtract(clientUserCardSetLimitRequest.getLimit()),
                clientUserCardSetLimitRequest.getStatus() ? userCard.getTransactionLimit().add(clientUserCardSetLimitRequest.getLimit()) : userCard.getTransactionLimit().subtract(clientUserCardSetLimitRequest.getLimit()),
                userCard.getChannelCardId()
        );
        return true;
    }

}



